package com.gitee.pagekit;

import com.gitee.pagekit.utils.PageUtil;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @author wxy
 * @version 1.0
 * @date 2024/5/11 17:35
 */
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class , Object.class, RowBounds.class, ResultHandler.class}),
})
public class MysqlPageInterceptor implements Interceptor {

    private static final Log log = LogFactory.getLog(MysqlPageInterceptor.class);

    private final PageProperties pageProperties;

    public MysqlPageInterceptor(PageProperties pageProperties) {
        this.pageProperties = pageProperties;
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        final Executor target = (Executor) invocation.getTarget();
        final Object[] args = invocation.getArgs();
        Object objet = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        MappedStatement mappedStatements = (MappedStatement) args[0];
        BoundSql boundSql = null;
        CacheKey cacheKey = null;
        if(args.length == 4){
            boundSql = mappedStatements.getBoundSql(objet);
            cacheKey = target.createCacheKey(mappedStatements, objet, rowBounds, boundSql);
        }else {
            boundSql = (BoundSql)args[5];
            cacheKey = (CacheKey) args[4];
        }

        final Object object = boundSql.getParameterObject();
        if(object instanceof Map){
            Map<String, Object> params = (Map<String, Object>) object;
            Page page = null;
            if((page = getPagByMap(params)) == null){
                return invocation.proceed();
            }
            page.startPage();
            PageUtil.initPage(page, pageProperties);
            /*
             * 开始分页
             * 1.拼接sql
             * 2.组装statement
             * 3.执行sql, 判断返回
             * 4.获取分页数据并返回
             */
            String sql = boundSql.getSql();
            PageCache pageCache = new PageCache();
            String countSql = "select count(1) from (" + boundSql.getSql() + ") as pageKit ";
            MappedStatement countMappedStatement = PageUtil.processCountMappedStatement(mappedStatements, mappedStatements.getId() + "_cunt", pageCache);
            final Long count = PageUtil.executeCountSql(countSql, target, countMappedStatement, objet, boundSql, (ResultHandler) args[3], pageCache);
            page.setCount(count);
            log.debug("count sql: " + countSql);
            if(count == 0L){
                page.endPage();
                return Collections.emptyList();
            }
            String pageSql = "select * from (" + boundSql.getSql() + ") as pageKit limit " + (page.getPageNum() - 1) * page.getPageSize()  + "," + page.getPageSize();
            List<?> pageResult = PageUtil.execPage(pageSql, boundSql, mappedStatements, rowBounds, target, objet, (ResultHandler) args[3], cacheKey);
            page.setList(pageResult);
            page.endPage();
            return pageResult;
        }

        return invocation.proceed();
    }

    private Page getPagByMap(Map<String, Object> params){
        for(String key : params.keySet()){
            final Object value = params.get(key);
            if(value == null){
                continue;
            }
            if(value instanceof Page){
                return (Page) value;
            }
        }
        return null;
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }



    @Override
    public Object plugin(Object target) {
        if(target instanceof Executor){
            return Plugin.wrap(target, this);
        }
        return target;
    }
}
